/***************************************************************************
 *   Copyright (C) 2009 by Zheng Yonglei
 *   zhengyonglei@gmail.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "Word.h"

#define VALID_MEMORY_HISTORY_SIZE 5

Word::Word():
	mID(0),
	mEnglish(""),
	mSoundMark(""),
	mIsActive(false),
	mIsFrozen(false),
	mPassLastTest(false),
	mFrozenRemainCycle(0),
	mDifficultyLevel(3),
	mMemoryLevel(0),
	mMemoryLevelDelta(0),
	mStatus(WS_Untouched)
{
}

Word::Word(int id):
		mID(id),
		mEnglish(""),
		mSoundMark(""),
		mIsActive(false),
		mIsFrozen(false),
		mFrozenRemainCycle(0),
		mDifficultyLevel(3),
		mMemoryLevel(0),
		mMemoryLevelDelta(0),
		mStatus(WS_Untouched)
{
}

TestMethod Word::selectTestMethod()
{
	//cout<<"Word::selectTestMethod"<<endl;
	int curMemoryLevelRate;
	curMemoryLevelRate = MEM_LEV_YN_RATE;
	int yesNoTest_Memorylevel = (mMemoryLevel%(curMemoryLevelRate*10))/curMemoryLevelRate;
	curMemoryLevelRate = MEM_LEV_MEG_RATE;
	int multiEngTest_Memorylevel = (mMemoryLevel%(curMemoryLevelRate*10))/curMemoryLevelRate;
	curMemoryLevelRate = MEM_LEV_MEX_RATE;
	int multiExpTest_Memorylevel = (mMemoryLevel%(curMemoryLevelRate*10))/curMemoryLevelRate;
	curMemoryLevelRate = MEM_LEV_SP_RATE;
	int spellTest_Memorylevel = (mMemoryLevel%(curMemoryLevelRate*10))/curMemoryLevelRate;

	if (yesNoTest_Memorylevel < ReciteTestPolity::getYesNoTestFinishCount(mImportanceLevel)
	&& OPT.useYesNoTestMethod)
		return yes_no_method;

	if (multiEngTest_Memorylevel < ReciteTestPolity::getMultiEngTestFinishCount(mImportanceLevel)
	&& OPT.useMultiEngTestMethod)
		return multi_English_choice_method;

	if (multiExpTest_Memorylevel < ReciteTestPolity::getMultiExpTestFinishCount(mImportanceLevel)
	&& OPT.useMultiExpTestMethod)
		return multi_exp_choice_method;

	if (spellTest_Memorylevel <= ReciteTestPolity::getSpellTestFinishCount(mImportanceLevel)
	&& OPT.useSpellTestMethod)
		return spell_method;

	//cout<<"random select availalbe"<<endl;
	//random select availalbe
	vector<TestMethod> methods;
	methods.clear();
	if (OPT.useYesNoTestMethod)
		methods.push_back(yes_no_method);
	if (OPT.useMultiEngTestMethod)
		methods.push_back(multi_English_choice_method);
	if (OPT.useMultiExpTestMethod)
		methods.push_back(multi_exp_choice_method);
	if (OPT.useSpellTestMethod)
		methods.push_back(spell_method);

	if (methods.empty())
	{
		cout<<"Word::selectTestMethod-->No test method available."<<endl;
		QCoreApplication::quit();
	}

	srand((int)time(0));
	int i=0+(int)(float(methods.size())*rand()/(RAND_MAX+1.0));
	return methods[i];
}

void Word::remember() //modify memory level
{
	mReciteHistory[QDate::currentDate()].remember();
	mPassLastTest=true;
	refineMemoryLevel();
	refineDifficultyLevel();
	checkIsFinished();
	setFrozenRemainCycle();
}

void Word::forget() //modify memory level
{
	mReciteHistory[QDate::currentDate()].forget();
	mPassLastTest=false;
	refineMemoryLevel();
	refineDifficultyLevel();
	mStatus = WS_Reciting;
	setFrozenRemainCycle();
}

void Word::checkIsFinished()
{
	cout<<"Word::checkIsFinished"<<endl;
	int curMemoryLevelRate;
	curMemoryLevelRate = MEM_LEV_YN_RATE;
	int yesNoTest_Memorylevel = (mMemoryLevel%(curMemoryLevelRate*10))/curMemoryLevelRate;
	curMemoryLevelRate = MEM_LEV_MEG_RATE;
	int multiEngTest_Memorylevel = (mMemoryLevel%(curMemoryLevelRate*10))/curMemoryLevelRate;
	curMemoryLevelRate = MEM_LEV_MEX_RATE;
	int multiExpTest_Memorylevel = (mMemoryLevel%(curMemoryLevelRate*10))/curMemoryLevelRate;
	curMemoryLevelRate = MEM_LEV_SP_RATE;
	int spellTest_Memorylevel = (mMemoryLevel%(curMemoryLevelRate*10))/curMemoryLevelRate;

	bool isFinished = true;
	if (	yesNoTest_Memorylevel < ReciteTestPolity::getYesNoTestFinishCount(mImportanceLevel)
	&& 	OPT.useYesNoTestMethod)
		isFinished = false;

	if (	multiEngTest_Memorylevel < ReciteTestPolity::getMultiEngTestFinishCount(mImportanceLevel)
	&& 	OPT.useMultiEngTestMethod)
		isFinished = false;

	if (	multiExpTest_Memorylevel < ReciteTestPolity::getMultiExpTestFinishCount(mImportanceLevel)
	&& 	OPT.useMultiExpTestMethod)
		isFinished = false;

	if (	spellTest_Memorylevel < ReciteTestPolity::getSpellTestFinishCount(mImportanceLevel)
	&& 	OPT.useSpellTestMethod)
		isFinished = false;

	if (isFinished)
	{
		mStatus = WS_Reviewing;
		mMemoryLevel = 0;
		mMemoryLevel += ReciteTestPolity::getYesNoTestFinishCount(mImportanceLevel)*MEM_LEV_YN_RATE;
		mMemoryLevel += ReciteTestPolity::getMultiEngTestFinishCount(mImportanceLevel)*MEM_LEV_MEG_RATE;
		mMemoryLevel += ReciteTestPolity::getMultiExpTestFinishCount(mImportanceLevel)*MEM_LEV_MEX_RATE;
		mMemoryLevel += ReciteTestPolity::getSpellTestFinishCount(mImportanceLevel)*MEM_LEV_SP_RATE;
		cout<<"Word::checkIsFinished-->This word is finished."<<endl;
	}
	else
	{
		mStatus = WS_Reciting;
		cout<<"Word::checkIsFinished-->This word is NOT finished."<<endl;
	}

	cout<<"Word::checkIsFinished-->mMemoryLevel="<<mMemoryLevel<<endl;
}

void Word::refineMemoryLevel()
{
	//float mlBackup = mMemoryLevel;
	vector<bool>& todayReciteHistory = mReciteHistory[QDate::currentDate()].getRemForHistory();
	bool testResult = todayReciteHistory[todayReciteHistory.size()-1];

	//This is a new memory level algorithm introduced in May 10, 2009
	int curMemoryLevelRate=1;
	int curMemoryLevelCeilingMax;
	switch(OPT.curTestMethod)
	{
	case yes_no_method:
		curMemoryLevelRate = MEM_LEV_YN_RATE;
		curMemoryLevelCeilingMax = MEM_LEV_YN_CEIL_MAX;
		break;
	case multi_exp_choice_method:
		curMemoryLevelRate = MEM_LEV_MEX_RATE;
		curMemoryLevelCeilingMax = MEM_LEV_MEX_CEIL_MAX;
		break;
	case multi_English_choice_method:
		curMemoryLevelRate = MEM_LEV_MEG_RATE;
		curMemoryLevelCeilingMax = MEM_LEV_MEG_CEIL_MAX;
		break;
	case spell_method:
		curMemoryLevelRate = MEM_LEV_SP_RATE;
		curMemoryLevelCeilingMax = MEM_LEV_SP_CEIL_MAX;
		break;
	default: //do nothing
		break;
	}

	mMemoryLevelDelta = curMemoryLevelRate*1;
	int curMemorylevel = (mMemoryLevel%(curMemoryLevelRate*10))/curMemoryLevelRate;
	if (testResult == true)
	{
		if (curMemorylevel+mMemoryLevelDelta/curMemoryLevelRate <= curMemoryLevelCeilingMax)
			mMemoryLevel += mMemoryLevelDelta;
	}
	else
	{
		if (curMemorylevel-mMemoryLevelDelta/curMemoryLevelRate >= 0)
			mMemoryLevel -= mMemoryLevelDelta;
	}
	cout	<<"Word::refineMemoryLevel-->new memory level calculation: "<<endl;
	cout	<<"mMemoryLevel="<<setw(4)<<setfill('0')<<mMemoryLevel
			<<"\tmMemoryLevelDelta="<<mMemoryLevelDelta<<endl;
	WLOG<<"Word::refineMemoryLevel-->new memory level calculation: "<<endl;
	WLOG<<"mMemoryLevel="<<setw(4)<<setfill('0')<<mMemoryLevel
			<<"\tmMemoryLevelDelta="<<mMemoryLevelDelta<<endl;
}

void Word::refineDifficultyLevel()
{
	//cout<<"Word::refineDifficultyLevel"<<endl;
	cout<<"Word::refineDifficultyLevel-->"<<mReciteHistory.size()<<" history items in all."<<endl;
	//This is a new algorithm introduced in May 6, 2009
	float firstRecord=0;
	float uncertainty=0;
	vector<int> diffLevHIstory;
	diffLevHIstory.clear();
	//int reciteCount;

	//cout<<"calculate firstRecord"<<endl;
	int reciteCount=0;
	float firstRecordWeight = 0;
	float firstRecordWeightRate=1;
	float uncertaintyWeight=0;
	float totalReciteCountWeight=0;
	float uncertaintyWeightRate=1;
	//map<QDate, ReciteDateHistory>::iterator history_it;
	for (	map<QDate, ReciteDateHistory>::iterator history_it = mReciteHistory.begin();
			history_it != mReciteHistory.end();
			history_it++)
	{
		cout<<"***************ReciteCount("<<reciteCount+1<<")*****************"<<endl;
		cout<<"ReciteDate="<<history_it->second.getDateString().toUtf8().data()<<endl;
		cout<<"RemForHistory="<<history_it->second.getRemForHistoryString().toUtf8().data()<<endl;
		//calculate firstRecord
		reciteCount++;
		vector<bool>& remForHistory = history_it->second.getRemForHistory();
		if (remForHistory[0] == false)
		{
			//cout<<"Word::refineDifficultyLevel-->recode["<<reciteCount-1<<"]="<<reciteCount
			//		<<"*"<<firstRecordWeightRate<<endl;
			firstRecordWeight += reciteCount*firstRecordWeightRate;
			//cout<<"Word::refineDifficultyLevel-->firstRecordWeight="<<firstRecordWeight<<endl;
		}

		firstRecord = firstRecordWeight/reciteCount;
		//cout<<"Word::refineDifficultyLevel-->reciteCount="<<reciteCount
		//cout<<"Word::refineDifficultyLevel-->firstRecord="<<firstRecord<<endl;

		//calculate uncertainty
		int n=0;
		int uncerWeightTemp=0;
		float failRate = float(history_it->second.getForgetCount())/float(history_it->second.getTotalReciteCount());
		float failRateWeight = 100;
		//cout<<"Word::refineDifficultyLevel-->failRate="<<failRate<<endl;
		float failRateExtra = 1.1;
		for (int i=0; i<remForHistory.size(); i++)
		{
			if (remForHistory[i] == false)
				n++;
			if (remForHistory[i] == true && n != 0)
			{
				//uncerWeightTemp+=uncertaintyWeightRate*n*n;
				failRate *= pow(failRateExtra, n-1);
				//cout<<"Word::refineDifficultyLevel-->failRate="<<failRate<<endl;
				n = 0;
				//cout<<"Word::refineDifficultyLevel-->uncerWeightTemp="<<uncerWeightTemp<<endl;
			}
		}
		if (n != 0)
		{
			//uncerWeightTemp+=uncertaintyWeightRate*n*n;
			failRate *= pow(failRateExtra, n-1);
			//cout<<"Word::refineDifficultyLevel-->failRate="<<failRate<<endl;
			//cout<<"Word::refineDifficultyLevel-->uncerWeightTemp="<<uncerWeightTemp<<endl;
		}

		//cout<<"Word::refineDifficultyLevel-->failRate="<<failRate<<endl;
		uncerWeightTemp += failRate*failRateWeight;
		//cout<<"Word::refineDifficultyLevel-->uncerWeightTemp="<<uncerWeightTemp<<endl;
		uncertaintyWeight += uncerWeightTemp*reciteCount*reciteCount*uncertaintyWeightRate;
		//cout<<"Word::refineDifficultyLevel-->uncertaintyWeight="<<uncertaintyWeight	<<endl;
		//totalReciteCountWeight += reciteCount*reciteCount*uncertaintyWeightRate;
		totalReciteCountWeight += reciteCount*reciteCount*uncertaintyWeightRate;

		uncertainty = uncertaintyWeight/totalReciteCountWeight;
		//cout<<"Word::refineDifficultyLevel-->totalReciteCountWeight="<<totalReciteCountWeight
		//		<<"\tuncertainty="<<uncertainty<<endl;

		//to sum up
		//int firstRecordSumRate = 10;
		//int firstRecordSumRate = 1;
		//int uncertaintySumRate = 10;
		//firstRecord *= firstRecordSumRate;
		//uncertainty *= uncertaintySumRate;
		if (firstRecord == 0)
			firstRecord = 0.1;
		if (uncertainty == 0)
			uncertainty = 0.1;

		int curDiffLevel = firstRecord*uncertainty;
		cout<<"Word::refineDifficultyLevel-->firstRecord="<<firstRecord<<endl;
		cout<<"Word::refineDifficultyLevel-->uncertainty="<<uncertainty<<endl;
		cout<<"Word::refineDifficultyLevel-->curDiffLevel="<<curDiffLevel<<endl;
		WLOG<<"Word::refineDifficultyLevel-->curDiffLevel="<<curDiffLevel<<endl;
		if (curDiffLevel<0)
		{
			cout<<"Word::refineDifficultyLevel-->Fatal error! Negative mDifficultyLevel"<<endl;
			QCoreApplication::quit();
		}

		diffLevHIstory.push_back(curDiffLevel);
	}
	mDifficultyLevel = diffLevHIstory[diffLevHIstory.size()-1];
}

void Word::setReciteDateHistory(QString dateHistoryArg)
{
	mReciteHistory.clear();

	QStringList dateList = dateHistoryArg.split(HISTORY_SEPARATOR, QString::SkipEmptyParts);
	if (dateList.isEmpty())
		return;

	WLOG<<"Word::setReciteDateHistory"<<endl;
	for (int i=0; i<dateList.size(); i++)
	{
		if (!dateList[i].contains(":"))
		{
			cout<<"Word::setReciteDateHistory-->Error. Illegal history format."<<endl;
			QCoreApplication::quit();
		}

		QString date = dateList[i].left(dateList[i].indexOf(":"));
		QString remForHistory =  dateList[i].right(dateList[i].size() - dateList[i].indexOf(":") -1);
		WLOG<<"history["<<i<<"]: date="<<date.toUtf8().data()<<"\tremForHistory="<<remForHistory.toUtf8().data()<<endl;
		mReciteHistory.insert(make_pair(QDate::fromString(date, "yyyy-MM-dd"), ReciteDateHistory(QDate::fromString(date, "yyyy-MM-dd"), remForHistory)));
	}
}

QString Word::getReciteDateHistoryString()
{
	//cout<<"Word::getReciteDateHistoryString"<<endl;
	QString historyString;
	historyString.clear();
	if (mReciteHistory.empty())
		return QString("");
	else
	{
		QStringList historyList;
		historyList.clear();
		map<QDate, ReciteDateHistory>::iterator history_it;
		for (history_it = mReciteHistory.begin(); history_it != mReciteHistory.end(); history_it++)
		{
			//cout<<"Word::getReciteDateHistoryString-->mReciteHistory["<<i<<"]="<<mReciteHistory[i]->getReciteDateHistoryString().toUtf8().data()<<endl;
			//historyString.append(mReciteHistory[i]->getReciteDateHistoryString() + QString(HISTORY_SEPARATOR));
			historyList.append(history_it->second.getReciteDateHistoryString());
		}
		historyString = historyList.join(HISTORY_SEPARATOR);

		//WLOG<<"Word::getReciteDateHistoryString-->historyString= "<<historyString.toUtf8().data()<<endl;
		return historyString;
	}
}

QStringList Word::getReciteDateString()
{
	QStringList list;
	map<QDate, ReciteDateHistory>::iterator history_it;
	for (history_it = mReciteHistory.begin(); history_it != mReciteHistory.end(); history_it++)
		list<<history_it->second.getDateString();

	return list;
}

int Word::getTotalReciteCount()
{
	int totalCount=0;
	map<QDate, ReciteDateHistory>::iterator history_it;
	for (history_it = mReciteHistory.begin(); history_it != mReciteHistory.end(); history_it++)
		totalCount += history_it->second.getTotalReciteCount();

	return totalCount;
}

float Word::getGrossPassRate()
{
	int rememberCount=0;
	int forgetCount=0;
	map<QDate, ReciteDateHistory>::iterator history_it;
	for (history_it = mReciteHistory.begin(); history_it != mReciteHistory.end(); history_it++)
	{
		rememberCount += history_it->second.getRememberCount();
		forgetCount += history_it->second.getForgetCount();
	}

	return float(float(rememberCount)/float(rememberCount+forgetCount));
}

int Word::getMemoryLevel(TestMethod testMethod)
{
	int curMemoryLevelRate;
	switch(testMethod)
	{
	case yes_no_method:
		curMemoryLevelRate = MEM_LEV_YN_RATE;
		return (mMemoryLevel%(curMemoryLevelRate*10))/curMemoryLevelRate;
	case multi_exp_choice_method:
		curMemoryLevelRate = MEM_LEV_MEX_RATE;
		return (mMemoryLevel%(curMemoryLevelRate*10))/curMemoryLevelRate;
	case multi_English_choice_method:
		curMemoryLevelRate = MEM_LEV_MEG_RATE;
		return (mMemoryLevel%(curMemoryLevelRate*10))/curMemoryLevelRate;
	case spell_method:
		curMemoryLevelRate = MEM_LEV_SP_RATE;
		return (mMemoryLevel%(curMemoryLevelRate*10))/curMemoryLevelRate;
	}
}

void Word::setRedBibleExp(QString Explanation)
{
	mExplanations[DictNames::GRERedBible][mEnglish] = Explanation;
}

QString Word::getExplanation(QString dictName)
{
	map<QString, map<QString, QString> >::iterator dict_it = mExplanations.find(dictName);
	if (dict_it == mExplanations.end())
	{
		cout<<"Word::getExplanation-->no article from "<<dictName.toUtf8().data()<<endl;
		return QString();
	}

	QString result;
	map<QString, QString> articles = dict_it->second;
	for (	map<QString, QString>::iterator article_it = articles.begin();
			article_it != articles.end();
			article_it++)
	{
		//result.append(article_it->second + QString("<BR>"));
		result.append(article_it->second);
	}

	return result;
}

map<QString, QString>& Word::getExplanationMap(QString dictName)
{
	map<QString, map<QString, QString> >::iterator dict_it = mExplanations.find(dictName);
	if (dict_it == mExplanations.end())
	{
		cout<<"Word::getExplanation-->no article from "<<dictName.toUtf8().data()<<endl;
		map<QString, QString> emptyMap;
		return emptyMap;
	}

	return dict_it->second;
}

QString Word::getRedBibleExp()
{
	map<QString, map<QString, QString> >::iterator exp_it;
	exp_it = mExplanations.find(DictNames::GRERedBible);
	if (exp_it == mExplanations.end())
	{
		//find in the database
		QString queryCommand;
		queryCommand.clear();
		queryCommand.append(QString("select exp_red_bible from words where id = %1;").arg(mID));
		DB.execQuery(queryCommand, QueryT_Read, DB.mUsrMain);
		QString result = DB.getTableValue(0,"exp_red_bible");
		if(!result.isEmpty())
		{
			mExplanations[DictNames::GRERedBible][mEnglish] = result;
			return result;
		}
		else
			return "";
	}
	else
		return mExplanations[DictNames::GRERedBible][mEnglish];
}

void Word::setWordStatus(int statusArg)
{
	if (statusArg == int(WS_Untouched))
		mStatus = WS_Untouched;
	else if(statusArg == int(WS_Reciting))
		mStatus = WS_Reciting;
	else if(statusArg == int(WS_Reviewing))
		mStatus = WS_Reviewing;
	else if(statusArg == int(WS_Finished))
		mStatus = WS_Finished;
	else
	{
		cout<<"Word::setWordStatus-->Invalid statusArg: "<<statusArg<<endl;
		QCoreApplication::quit();
	}
}

QString Word::getWordStatusString()
{
	if (mStatus == WS_Untouched)
		return "WS_Untouched";
	else if(mStatus == WS_Reciting)
		return "WS_Reciting";
	else if(mStatus == WS_Reviewing)
		return "WS_Reviewing";
	else if(mStatus == WS_Finished)
		return "WS_Finished";
	else if(mStatus == WS_Associate)
		return "WS_Associate";
	else
	{
		cout<<"Word::getWordStatusString-->invalid mStatus."<<endl;
		QCoreApplication::quit();
	}
}

void Word::setFrozenRemainCycle()
{
	mFrozenRemainCycle = 2;
	mFrozenRemainCycle = WM.getWordCountByStatus(mStatus)/2;

	if(mFrozenRemainCycle < 4)
	{
		if(mDifficultyLevel < 5)
			mFrozenRemainCycle = 4;
		else if (mDifficultyLevel < 8)
			mFrozenRemainCycle = 3;
		else
			mFrozenRemainCycle = 2;
	}

	cout<<"Word::setFrozenRemainCycle-->mFrozenRemainCycle = "<<mFrozenRemainCycle<<endl;
}

ReciteDateHistory::ReciteDateHistory():
	mRememberCount(0),
	mForgetCount(0)
{
	mReciteDate = QDate::currentDate();
	mDateString = mReciteDate.toString("yyyy-MM-dd");
	mReciteDateString = mDateString + QString(":");
	mRemForHistory.clear();
	mRemForHistoryString.clear();
}

ReciteDateHistory::ReciteDateHistory(QDate date, QString remForHistoryString):
	mReciteDate(date),
	mRemForHistoryString(remForHistoryString),
	mRememberCount(0),
	mForgetCount(0)
{
	mDateString = mReciteDate.toString("yyyy-MM-dd");
	mReciteDateString = mDateString + QString(":") + mRemForHistoryString;
	decodeReciteHistory();
}

int ReciteDateHistory::getInterval()
{
	QDate today = QDate::currentDate();
	int interval = mReciteDate.daysTo(today);
	return interval;
}

void ReciteDateHistory::remember()
{
	//cout<<"ReciteDateHistory::remember"<<endl;
	mRemForHistory.push_back(true);
	mRememberCount++;

	//cout<<"ReciteDateHistory::remember-->mRemForHistoryString="<<mRemForHistoryString.toUtf8().data()<<endl;
	mRemForHistoryString = encodeReciteHistory();
	//cout<<"ReciteDateHistory::remember-->mRemForHistoryString="<<mRemForHistoryString.toUtf8().data()<<endl;
	mReciteDateString = mDateString + QString(":") + mRemForHistoryString;
}

void ReciteDateHistory::forget()
{
	mRemForHistory.push_back(false);
	mForgetCount++;

	mRemForHistoryString = encodeReciteHistory();
	mReciteDateString = mDateString + QString(":") + mRemForHistoryString;
}

QString ReciteDateHistory::encodeReciteHistory()
{
	//cout<<"ReciteDateHistory::encodeReciteHistory"<<endl;
	QString newHistoryString;
	//encode
	bool lastItem;
	int seriCount;
	if (mRemForHistory[0] == true)
	{
		lastItem = true;
		seriCount = 1;
	}
	else
	{
		lastItem = false;
		seriCount = 1;
	}

	for (int j=1; j<mRemForHistory.size(); j++)
	{
		if (mRemForHistory[j] == lastItem)
			seriCount++;
		else
		{
			if (lastItem == true)
				newHistoryString.append(QString("Y%1").arg(seriCount));
			else
				newHistoryString.append(QString("N%1").arg(seriCount));

			lastItem = mRemForHistory[j];
			seriCount = 1;
		}
	}
	if (lastItem == true)
		newHistoryString.append(QString("Y%1").arg(seriCount));
	else
		newHistoryString.append(QString("N%1").arg(seriCount));
	//cout<<"Word::encodeReciteHistory-->New string = "<<newHistoryString.toUtf8().data()<<endl;

	return newHistoryString;
}

void ReciteDateHistory::decodeReciteHistory()
{
	//cout<<"ReciteDateHistory::decodeReciteHistory"<<endl;
	mRemForHistory.clear();
	mRememberCount = 0;
	mForgetCount = 0;

	bool lastItem = true;
	int seriCount = 0;
	QString count;
	count.clear();
	for (int j=0; j<mRemForHistoryString.size(); j++)
	{
		if (mRemForHistoryString[j] == 'Y' || mRemForHistoryString[j] == 'N')
		{
			if (!count.isEmpty())
				for (int k=0; k<count.toInt(); k++)
				{
					mRemForHistory.push_back(lastItem);
					if (lastItem == true)
						mRememberCount++;
					else
						mForgetCount++;
				}

			if (mRemForHistoryString[j] == 'Y')
			{
				lastItem = true;
				count.clear();
			}
			else
			{
				lastItem = false;
				count.clear();
			}
			seriCount = 1;
		}
		else
			count.append(mRemForHistoryString[j]);
	}
	if (!count.isEmpty())
		for (int k=0; k<count.toInt(); k++)
		{
			mRemForHistory.push_back(lastItem);
			if (lastItem == true)
				mRememberCount++;
			else
				mForgetCount++;
		}

	/*
	cout<<"decodeHistory=";
	for (int j=0; j<mRemForHistory.size(); j++)
	{
		if (mRemForHistory[j] == true)
			cout<<"1";
		else
			cout<<"0";
	}
	cout<<endl;
	*/
}

int ReciteTestPolity::getYesNoTestFinishCount(int impLevel)
{
	//if (impLevel <= 2)
	//	return 1;
	//else
	return 2;
}

int ReciteTestPolity::getMultiEngTestFinishCount(int impLevel)
{
	return 1;
}

int ReciteTestPolity::getMultiExpTestFinishCount(int impLevel)
{
	return 1;
}

int ReciteTestPolity::getSpellTestFinishCount(int impLevel)
{
	if (impLevel <= 2)
		return 0;
	else
		return 1;
}
